#include <stdio.h>
#include <conio.h>
#include <time.h>
#include <stdlib.h>

#define TRUE 1
#define FALSE 0

enum GAMESTATE
{
   START,
   END
}; 

enum GAMEERROR
{
    NOERROR, 
    INVALIDINPUT,
    NOTEMPTYCELL       
};

enum GAMERESULT
{
   NORESULT,
   USERWIN,
   AIWIN,
   DRAW      
};  
      
void initializeBoard(void);         /* Initializing the value into ' ' in every array */
void printBoard(void);              /* Output the 3x3 grid                            */
void intro(void);                   /* Introduction for the tic-tac-toe               */
int checkInput(int input);          /* Checking the Input if it's valid or invalid    */
int inputPlayer(void);              
void settingXO(int input,char XO);
int checkWin(char XO);
int checkWinningMove(char XO);
void inputAI(void);
int isDraw(void);
void clearTheScreen(void);
int isEmpty(int input);
void printErrors(enum GAMEERROR error);
void printResult(enum GAMERESULT result);

char field[3][3];                   /*Global variable for the 3x3 grid for tic-tac-toe */

int main(void)
{
   enum GAMESTATE state;
   enum GAMEERROR error;
   enum GAMERESULT result;
   int input;
   int AImove,userMove;
   initializeBoard();
   intro();
   state = START;
   error = NOERROR;
   result = NORESULT;
   while(state == START)
   { 
      clearTheScreen();
      printErrors(error);

      error = NOERROR;
      printBoard(); 
      fflush(stdin);
      input = inputPlayer();
      if(checkInput(input) == TRUE)
      {
         if(isEmpty(input) == TRUE)
         {  
             settingXO(input,'X');
             if(checkWin('X') == TRUE)
             {
                result = USERWIN;                              
                state = END;
                clearTheScreen();
                printResult(result);                
                printBoard();               
             }
             else if(isDraw() == TRUE)
             {
                result = DRAW;                  
                state = END;
                clearTheScreen();
                printResult(result);
                printBoard();
             }
             else
             {
                userMove = checkWinningMove('X');
                if(userMove != 0)
                {         
                   settingXO(userMove,'O'); 
                } 
                else
                {
                   AImove = checkWinningMove('O');
                   if(AImove == 0)
                   {
                      inputAI();                
                   }
                   else
                   {
                       settingXO(AImove,'O');
                   } 
                } 
                if(checkWin('O') == TRUE)
                {
                   result = AIWIN;                                  
                   state = END;
                   clearTheScreen();
                   printResult(result);
                   printBoard();            
                }            
                else if(isDraw() == TRUE)
                {
                   result = DRAW;  
                   state = END;
                   clearTheScreen();
                   printResult(result);
                   printBoard();  
                }  
             }
         }
         else
         {
            error = NOTEMPTYCELL;      
         }                                         
      }
      else
      {
         error = INVALIDINPUT;           
      }
   }
    
    
   getch(); 
   return 0;     
}

/*
 * Description:
 *   This function will print the game result.
 *
 * Parameter:
 *   result - enumaration of GAMERESULT
 *
 * Return:
 *   nothing
 */
void printResult(enum GAMERESULT result)
{
   switch(result)
   {
      case USERWIN:
         printf("\nYOU WIN!\n\n");
         break;
      case AIWIN:
         printf("\nYOU LOSE!\n\n");
         break;
      case DRAW:
         printf("\nTHE GAME IS DRAW!\n\n");                                        
   }     
}

/*
 * Description:
 *   This function will print the game errors
 *
 * Parameter:
 *   error - enumaration of GAMERERROR
 *
 * Return:
 *   nothing
 */
void printErrors(enum GAMEERROR error)
{
   switch(error)
   {
      case INVALIDINPUT:
         printf("INVALID INPUT!\n\n");          
         break;
      case NOTEMPTYCELL:
         printf("NOT EMPTY CELL!\n\n");         
   }     
}

/*
 * Description:
 *   This function will determine if the game is draw.
 *
 * Parameter:
 *   nothing
 *
 * Return:
 *   int - 
 *      TRUE if field are already fully occupied.
 *      FALSE if one of the cells are not occupied.
 *
 */
int isDraw(void)
{
   int i,j;
   for(i=0;i<3;i++)
   {
      for(j=0;j<3;j++)
      {
         if(field[i][j] == ' ')
         {
            return FALSE;               
         }                
      }                
                   
   }
   return TRUE;              
}

/*
 * Description:
 *   This function will be the responsible for AI moves.
 *
 * Parameter:
 *   nothing
 *
 * Return:
 *   nothing
 */
void inputAI(void)
{
   int x,y; 
   while(TRUE)
   {
     srand(time(NULL));
     x = rand() % 3;
     y = rand() % 3;           
     if(field[x][y] == ' ')
     {
        break;
     }            
   }    
   field[x][y] = 'O';
   
}

/*
 * Description:
 *   This function will check the designated number if it's an empty cell. 
 *
 * Parameter:
 *   input - the number that input by player/AI.
 *
 * Return:
 *   int - returnValue
 *     TRUE if the cell is empty.
 *     FALSE if not.     
 */
int isEmpty(int input)
{
   int x,y,returnValue = FALSE;
   switch(input)
   {
     case 1:
        x = 0;
        y = 0;
        break;
     case 2:
        x = 0;
        y = 1;
        break;
     case 3:
        x = 0;
        y = 2;
        break;
     case 4:
        x = 1;
        y = 0;
        break;
     case 5:
        x = 1;
        y = 1;
        break;
     case 6:
        x = 1;
        y = 2;
        break;
     case 7:
        x = 2;
        y = 0;
        break;
     case 8:
        x = 2;
        y = 1;
        break;
     case 9:
        x = 2;
        y = 2;
        break;
  }
  if(field[x][y] == ' ')
  {
     returnValue = TRUE;                  
  }
  return returnValue;
}

/*
 * Description:
 *   This function will check the winning move for player/AI.
 *   If it's for player, the AI will block the winning move.
 *
 * Parameter:
 *   XO - the X and O.
 *
 * Return:
 *   int - returnValue
 *      it returns the winning move of the player or the AI.
 *         
 */
int checkWinningMove(char XO)
{
   int returnValue = 0;
   if((field[0][0] == XO && field[0][1] == XO) ||
      (field[1][2] == XO && field[2][2] == XO) ||
      (field[1][1] == XO && field[2][0] == XO))
   {
     if(field[0][2] == ' ')
     {
        returnValue = 3;                            
     }
   }    
   else if((field[0][1] == XO && field[0][2] == XO) || // x 2 3
           (field[1][0] == XO && field[2][0] == XO) || // x 4 7
           (field[1][1] == XO && field[2][2] == XO))   // x 5 9
   {
      if(field[0][0] == ' ')
      {            
         returnValue = 1;                            
      }
   }
   else if((field[0][0] == XO && field[0][2] == XO) ||
           (field[1][1] == XO && field[2][1] == XO))
   {
      if(field[0][1] == ' ')        
         returnValue = 2;                            
   }
   else if((field[1][1] == XO && field[1][2] == XO) ||
           (field[0][0] == XO && field[2][0] == XO))
   {
      if(field[1][0] == ' ')        
         returnValue = 4;                            
   }
   else if((field[1][0] == XO && field[1][2] == XO) ||
           (field[0][1] == XO && field[2][1] == XO) ||
           (field[0][0] == XO && field[2][2] == XO) ||
           (field[0][2] == XO && field[2][0] == XO))
   {
      if(field[1][1] == ' ')        
         returnValue = 5;                            
   }
   else if((field[1][0] == XO && field[1][1] == XO) ||
           (field[0][2] == XO && field[2][2] == XO))
   {
      if(field[1][2] == ' ')        
         returnValue = 6;                            
   }          
   else if((field[2][1] == XO && field[2][2] == XO) ||
           (field[0][0] == XO && field[1][0] == XO) ||
           (field[1][1] == XO && field[0][2] == XO))
   {
      if(field[2][0] == ' ')        
         returnValue = 7;                            
   }
   else if((field[2][0] == XO && field[2][2] == XO) ||
           (field[0][1] == XO && field[1][1] == XO))
   {
      if(field[2][1] == ' ')        
         returnValue = 8;                            
   }
   else if((field[2][0] == XO && field[2][1] == XO) ||
           (field[0][2] == XO && field[1][2] == XO) ||
           (field[0][0] == XO && field[1][1] == XO))
   {
      if(field[2][2] == ' ')        
         returnValue = 9;                            
   }
   else
   {
      returnValue = 0;    
   }
   return returnValue;                     
                                     
}

/*
 * Description:
 *   This function will check the cells to know it there's a winner. 
 *
 * Parameter:
 *   XO - the X and O.
 *
 * Return:
 *   int - 
 *     TRUE if there's a winner.
 *     FALSE if not.    
 * 
 */
int checkWin(char XO)
{
   if(field[0][0] == XO && field[0][1] == XO && field[0][2] == XO)
   {
      return TRUE;               
   }
   else if(field[1][0] == XO && field[1][1] == XO && field[1][2] == XO)
   {
      return TRUE;     
   }    
   else if(field[2][0] == XO && field[2][1] == XO && field[2][2] == XO)    
   {
      return TRUE;     
   }
   else if(field[0][0] == XO && field[1][0] == XO && field[2][0] == XO)    
   {
      return TRUE;     
   }
   else if(field[0][1] == XO && field[1][1] == XO && field[2][1] == XO)    
   {
      return TRUE;     
   }
   else if(field[0][2] == XO && field[1][2] == XO && field[2][2] == XO)    
   {
      return TRUE;     
   }
   else if(field[0][0] == XO && field[1][1] == XO && field[2][2] == XO)    
   {
      return TRUE;     
   }
   else if(field[0][2] == XO && field[1][1] == XO && field[2][0] == XO)    
   {
      return TRUE;     
   }
   
   return FALSE;           
}

/*
 * Description:
 *   This function will check the player's input number.
 *
 * Parameter:
 *   input - player's input number.
 *
 * Return:
 *   int - returnValue 
 *     TRUE if the input number is 0-9.
 *     FALSE if not.    
 */
int checkInput(int input)
{
   int returnValue = FALSE;
   if(input <= 9 && input >= 1)
   {
      returnValue = TRUE;         
   }                 
   return returnValue;  
}

/*
 * Description:
 *   This function will input the player's turn.
 *
 * Parameter:
 *   nothing
 *
 * Return:
 *   int - input    
 */
int inputPlayer(void)
{
   int input;
   printf("Your turn: ");
   scanf("%d",&input);
   
   return input;    
}

/*
 * Description:
 *   This function will initialize the value of every cells into ' '. 
 *
 * Parameter:
 *   nothing
 *
 * Return:
 *   nothing
 */
void initializeBoard(void)
{
   int i,j;

   for(i=0;i<3;i++) 
       for(j=0;j<3;j++)
          field[i][j]=' ';    
}

/*
 * Description:
 *   This function will print the current board. 
 *
 * Parameter:
 *   nothing
 *
 * Return:
 *   nothing
 */
void printBoard(void)
{
   int i,j,counter=1;
   for(i=0;i<3;i++)
   {
      for(j=0;j<3;j++)
      {
         if(field[i][j] != ' ')
            printf("[ %c ]",field[i][j]);
         else
            printf("[ %d ]",counter);
         counter++;
      }
      printf("\n");
   }         
}

/*
 * Description:
 *   This function will give a short introduction for the game.
 *
 * Parameter:
 *   nothing
 *
 * Return:
 *   nothing
 */
void intro(void)
{
     printf("-------------------------------------------------------------------------------\n");
     printf("| Have you got the tactical know-how to become the Tic Tac Toe Champ in Town? |\n"); 
     printf("| Sometimes, the simplest games can be the most fun!                          |\n");
     printf("| Get three X's in a row before the computer gets three O's in a row.         |\n");
     printf("|                                                                             |\n");
     printf("| HOW TO PLAY THE GAME:                                                       |\n");
     printf("| 1. Each square corresponds to a number                                      |\n");
     printf("| 2. To place an X on a square, you must choose and input a number from 1-9   |\n");
     printf("| 3. The computer will randomly place an O on the board after you place an X. |\n");
     printf("| 4. Enjoy the game and press any key to START!                               |\n");
     printf("-------------------------------------------------------------------------------\n");     
     getch();
}

/*
 * Description:
 *   This function will set the X/O in the designated cell number.
 *
 * Parameter:
 *   input - the cell number,
 *   XO - the X and O.
 *
 * Return:
 *   nothing
 */
void settingXO(int input,char XO)
{
      int x,y;
      switch(input)
      {
         case 1:
            x = 0;
            y = 0;
            break;
         case 2:
            x = 0;
            y = 1;
            break;
         case 3:
            x = 0;
            y = 2;
            break;
         case 4:
            x = 1;
            y = 0;
            break;
         case 5:
            x = 1;
            y = 1;
            break;
         case 6:
            x = 1;
            y = 2;
            break;
         case 7:
            x = 2;
            y = 0;
            break;
         case 8:
            x = 2;
            y = 1;
            break;
         case 9:
            x = 2;
            y = 2;
            break;
      }
      field[x][y] = XO;           
}

/*
 * Description:
 *    This function will clear the screen.
 * 
 * Parameter:
 *   nothing
 *
 * Return:
 *   nothing
 */
void clearTheScreen(void)
{
   /*int i;
   for(i=0;i<25;i++)
   {
      printf("\n");                                   
   }*/
   system("cls");     
}
